package pl.kosmowska.ewelina.client.app.activity;


import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import pl.kosmowska.ewelina.client.Constants;
import pl.kosmowska.ewelina.client.ViewMessages;
import pl.kosmowska.ewelina.client.app.ClientFactory;
import pl.kosmowska.ewelina.client.app.activity.places.LoanEditPlace;
import pl.kosmowska.ewelina.client.app.activity.places.LoanPlace;
import pl.kosmowska.ewelina.client.app.activity.places.SignInPlace;
import pl.kosmowska.ewelina.client.app.requestfactory.LoanDataRequest;
import pl.kosmowska.ewelina.client.app.requestfactory.dto.LoanDataProxy;
import pl.kosmowska.ewelina.client.app.requestfactory.dto.OutcomeDataProxy;
import pl.kosmowska.ewelina.client.app.user.AuthEvent;
import pl.kosmowska.ewelina.client.app.user.AuthEventHandler;
import pl.kosmowska.ewelina.client.app.user.AuthEvent.Auth;
import pl.kosmowska.ewelina.client.views.LoanEditView;
import pl.kosmowska.ewelina.client.views.impl.LoanEditViewImpl;
import pl.kosmowska.ewelina.client.views.impl.beans.Installment;

import com.google.gwt.activity.shared.AbstractActivity;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.place.shared.Place;
import com.google.gwt.user.client.ui.AcceptsOneWidget;
import com.google.web.bindery.requestfactory.shared.EntityProxyId;
import com.google.web.bindery.requestfactory.shared.Receiver;
import com.google.web.bindery.requestfactory.shared.Request;
import com.google.web.bindery.requestfactory.shared.ServerFailure;

public class LoanEditActivity extends AbstractActivity implements LoanEditView.Presenter {

	private LoanEditView view;

	private ClientFactory clientFactory;

	private LoanDataProxy loanData;

	private EntityProxyId<LoanDataProxy> loanDataId;
	// get the requestContext
	LoanDataRequest loanRequest;
	Loan loan;

	public LoanEditActivity(LoanEditPlace place, ClientFactory clientFactory) {
		this.loanData = place.getLoanData();
		this.loanDataId = place.getLoanDataId();
		this.clientFactory = clientFactory;
		loanRequest = clientFactory.getRequestFactory().getLoanDataRequest();
	}

	/**
	 * Invoked by the ActivityManager to start a new Activity
	 */
	@Override
	public void start(AcceptsOneWidget containerWidget, EventBus eventBus) {
		view = clientFactory.getLoanEditView();
		view.setPresenter(this);
		initAuthorisationForView();
		if (loanDataId != null) { 
			getLoanData();

		} else {
			view.setData(loanData);
			startView();
		}		
		containerWidget.setWidget(view.asWidget());
	}
	
	private void startView() {
		if (clientFactory.getIsLoggedIn() == null) {
			// wait for login event b/c hasn't happened yet
		} else if (clientFactory.getIsLoggedIn() == true) {
			view.reInitView();
		} else if (clientFactory.getIsLoggedIn() == false) { 
			goTo(new SignInPlace());
		}	
	}
	
	private void initAuthorisationForView() {
		if (view.isAlreadyInit() == false) {
			clientFactory.getEventBus().addHandler(AuthEvent.TYPE, new AuthEventHandler() {
				public void onAuthEvent(AuthEvent event) {
					Auth e = event.getAuthEvent();
					if (e == Auth.LOGGEDIN) {
						view.reInitView();
					} else if (e == Auth.LOGGEDOUT) {
						goTo(new SignInPlace());
					}
				}
			});
		}
		view.setAlreadyInit(true);
	}

	private void getLoanData() {
		Request<LoanDataProxy> request = clientFactory.getRequestFactory().getLoanDataRequest().find(loanDataId);
		request.fire(new Receiver<LoanDataProxy>() {
			public void onSuccess(LoanDataProxy loanData) {
				setData(loanData);
				view.setData(loanData);
				startView();
			}

			public void onFailure(ServerFailure error) {
				super.onFailure(error);
			}
		});

	}

	private void setData(LoanDataProxy loanData) {
		this.loanData = loanData;

	}

	public void goTo(Place place) {
		clientFactory.getPlaceController().goTo(place);
	}

	@Override
	public void saveLoanData() {
		// is it create or edit
		LoanDataProxy data = null;
		if (loanData == null) { // insert|create
			data = loanRequest.create(LoanDataProxy.class);

		} else { // update|edit
			data = loanRequest.edit(loanData);
		}

		// persist these
		data.setName(loan.getName());
		data.setValue(loan.getValue());
		data.setValueToPay((float) round(loan.getInterestAmount()+loan.getLoanTotalAmount()));
		data.setMonthNumber(loan.getMonthNumber());
		data.setFee(loan.getFee());
		data.setPercentage(loan.getPercentage());
		data.setInstallmentChoice(loan.getInstallmentChoice());
		data.setStartDate(loan.getStartDate());
		data.setFeeChoice(loan.getFeeChoice());
		data.setEndDate(loan.getLastInstallmentDate());
		data.setOtherPayments(loan.getOtherPayments());


		loanRequest.persist(data, loan.getOutcomeDataProxy()).fire(new Receiver<LoanDataProxy>() {
			public void onSuccess(LoanDataProxy loanData) {
				clientFactory.getPlaceController().goTo(new LoanPlace());
			}
			public void onFailure(ServerFailure error) {
				view.showMessage(ViewMessages.ERROR_ON_PERSIST);
				super.onFailure(error);
			}
		}); 
	}


	public OutcomeDataProxy getOutcomeProxy(String name, Date repeatEndDate,
			Date date, float value) {
		OutcomeDataProxy outcomeData = loanRequest.create(OutcomeDataProxy.class);
		outcomeData.setDate(date);
		outcomeData.setCategory(Constants.LOAN_CATEGORY);
		outcomeData.setMultiple(true);
		outcomeData.setPriority(Constants.OUTCOME_PRIORITY_HIGH);
		outcomeData.setValue(value);
		outcomeData.setRepeatInterval(Constants.OUTCOME_FREQUENCY_MONTH);
		outcomeData.setName(name);
		outcomeData.setRepeatEndDate(repeatEndDate);
		return outcomeData;
	}

	@Override
	public void goToLoanPlace() {
		goTo(new LoanPlace());
		
	}
	public class Loan{
		ArrayList <Installment >installmentList;
		Date lastInstallmentDate;
		String name;
		float value; 
		float percentage; 
		float fee; 
		Date startDate; 
		int feeChoice; 
		int installmentChoice; 
		int monthNumber;
		float loanCapital;
		float clientCapital;
		float feePayment;
		float otherPayments;

		public String getName() {
			return name;
		}

		public void setName(String name) {
			this.name = name;
		}

		public float getValue() {
			return value;
		}

		public void setValue(float value) {
			this.value = value;
		}
		public float getPercentage() {
			return percentage;
		}

		public void setPercentage(float percentage) {
			this.percentage = percentage;
		}
		public float getFee() {
			return fee;
		}

		public void setFee(float fee) {
			this.fee = fee;
		}

		public Date getStartDate() {
			return startDate;
		}

		public void setStartDate(Date startDate) {
			this.startDate = startDate;
		}

		public int getFeeChoice() {
			return feeChoice;
		}

		public void setFeeChoice(int feeChoice) {
			this.feeChoice = feeChoice;
		}

		public int getInstallmentChoice() {
			return installmentChoice;
		}

		public void setInstallmentChoice(int installmentChoice) {
			this.installmentChoice = installmentChoice;
		}

		public int getMonthNumber() {
			return monthNumber;
		}

		public void setMonthNumber(int monthNumber) {
			this.monthNumber = monthNumber;
		}

		public void setFeePayment(float feePayment) {
			this.feePayment = feePayment;
		}
		public float getOtherPayments() {
			return otherPayments;
		}

		public float getLoanCapital() {
			return loanCapital;
		}

		public float getClientCapital() {
			return clientCapital;
		}

		public float getFeePayment() {
			return feePayment;
		}

		public ArrayList<Installment> getInstallmentList() {
			return installmentList;
		}

		public Date getLastInstallmentDate() {
			return lastInstallmentDate;
		}

		public void setLastInstallmentDate(Date lastInstallmentDate) {
			this.lastInstallmentDate = lastInstallmentDate;
		}
		public Loan(String name, float value, float percentage, float fee, Date startDate, int feeChoice, int installmentChoice, int monthNumber, float otherPayments){
			installmentList= new ArrayList <Installment > ();
			this.name=name;
			this.value=value; 
			this.percentage=percentage; 
			this.fee=fee; 
			this.startDate=startDate; 
			this.feeChoice=feeChoice; 
			this.installmentChoice=installmentChoice; 
			this.monthNumber=monthNumber;
			this.otherPayments=otherPayments;


			switch(feeChoice){
			case 0 : feePayment=0; loanCapital=value; clientCapital=value; break;
			case 1 : feePayment=(float)(value*(fee*0.01)); loanCapital=(float)(value*(1+fee*0.01)); clientCapital=value; break;
			case 2 : feePayment=(float)(value*(fee*0.01)); loanCapital=value; clientCapital=(float)(value*(1-fee*0.01)); break;
			case 3 : feePayment=(float)(value*(fee*0.01)); loanCapital=value; clientCapital= value; break;
			}
		}

		public float getLoanTotalAmount(){
			float sum=0;
			for (Installment installment: installmentList){
				sum+=installment.getCapital()+installment.getInterest();
			}
			return sum;
		}

		public float getInterestAmount(){
			float sum=0;
			for (Installment installment: installmentList){
				sum+=installment.getInterest();
			}
			return (float) round( sum);
		}
		public void generateInstallmentList(){
			if(installmentChoice==0)
				generateConstantValueInstallmentList();
			else
				generateVariableValueInstallmentList();		
		}

		private void generateConstantValueInstallmentList(){
			//	Rata = K * y^n * (y-1) / (y^n-1) 
			installmentList.clear();
			float monthPercentage = (float)(percentage*0.01)/12;
			float y=1+monthPercentage;
			double currentY=1;

			for(int i=0; i<monthNumber;i++){
				currentY*=y;
			}
			Date currentDate=new Date (startDate.getTime());
			float currentValue=loanCapital;
			float installmentValue=(float) round ((float)(loanCapital*currentY*monthPercentage/(currentY-1)));
			for(int i=0; i<monthNumber; i++){
				float interest= (float) round( currentValue*monthPercentage);			
				float capital=(float) round(installmentValue-interest);
				currentValue-=installmentValue;
				Installment newInstallment= new Installment(i+1, capital,interest, new Date(currentDate.getTime()));
				installmentList.add(newInstallment);
				if (currentDate.getMonth()==11)
					currentDate.setYear(currentDate.getYear()+1);	
				currentDate.setMonth((currentDate.getMonth()+1)%12);
			}

			if (currentDate.getMonth()==0){
				currentDate.setYear(currentDate.getYear()-1);	
				currentDate.setMonth(11);
			}else{
				currentDate.setMonth(currentDate.getMonth()-1);
			}

			lastInstallmentDate=currentDate;

		}
		private void generateVariableValueInstallmentList(){
			installmentList.clear();
			float currentValue=loanCapital;
			Date currentDate=new Date (startDate.getTime());
			float capital = (float) round((float)(loanCapital/monthNumber));
			float monthPercentage = (float)(percentage*0.01)/12;
			for(int i=0; i<monthNumber; i++){
				float interest= (float) round( currentValue*monthPercentage);
				Installment newInstallment= new Installment(i+1, capital,interest,new Date(currentDate.getTime()));
				installmentList.add(newInstallment);
				currentValue-=capital;
				if (currentDate.getMonth()==11)
					currentDate.setYear(currentDate.getYear()+1);	
				currentDate.setMonth((currentDate.getMonth()+1)%12);


			}

			if (currentDate.getMonth()==0){
				currentDate.setYear(currentDate.getYear()-1);	
				currentDate.setMonth(11);
			}else{
				currentDate.setMonth(currentDate.getMonth()-1);
			}

			lastInstallmentDate=currentDate;

		}

		public List<OutcomeDataProxy> getOutcomeDataProxy() {
			List <OutcomeDataProxy >outcomeDataList = new ArrayList <OutcomeDataProxy >();			
			if ((installmentList==null)||(installmentList.isEmpty())){
				return null;
			}
			for (Installment installment: installmentList){
				outcomeDataList.add( getOutcomeProxy(name,lastInstallmentDate, installment.getDate(), -(installment.getCapital()+ installment.getInterest())));
			}
			return outcomeDataList;
		}
	}


	private double round (double val)
	{
		double exp = Math.pow(10, 2);
		double rounded = val * exp;
		rounded = Math.round(rounded);
		return rounded / exp;
	}


	@Override
	public void generateLoan(String name, float value, float percentage,
			Date startDate, int feeChoice, float fee, int installmentChoice,
			int monthNumber, float otherPayments) {
		loan=new Loan(name, value, percentage, fee, startDate, feeChoice, installmentChoice, monthNumber, otherPayments);
		loan.generateInstallmentList();

		List<Installment> loanList=loan.getInstallmentList();
		if((loanList==null)||(loanList.isEmpty())){
			view.showMessage(ViewMessages.ERROR_WRONG_INPUT);
			return;
		}

		view.showSummary(loan.getClientCapital(),loan.getLoanCapital(), loan.getFeePayment(), loan.getOtherPayments(), loan.getLoanTotalAmount());
		view.showGrid(loan.getInstallmentList());
		view.loadChart(loan.getClientCapital(), loan.getInterestAmount(), loan.getFeePayment());
		
	}
}